/**
 * Creates the code for finding an element; for instance, in the Javacode
 *          engine.button(value("Button Text"));
 * 'name("btnName") is the Finder.
 *
 * In xml this would be expressed as
 * <tf:button>
 <tf:Finder>
 <tf:value value="Button Text" />
 </tf:Finder>
 <tf:click />
 </tf:button>
 */

FinderBuilder = {

/**
 * At this point we do not do chaining of commands; if we DO require chaining (we will), then it will be
 * internal to this build function.
 */
    build: function( element, htmlFinder, htmlFinderName, xmlElName, args )
    {
        try
        {
            var isFrame = this.isFrame(element);

            if ( element == FirebugContext.window || isFrame )
            {
                xmlElName = "BrowserCommand";
//                return this.buildBrowserCommand(element, htmlFinder, htmlFinderName, xmlElName, args);
            }
            else if ( !xmlElName )
            {
                xmlElName = "ElementAction";
            }

            /* Find what window we are in and determine which 'engine' (browser) to use for commands.*/

            var objKey;
            if(args.isFrame)
            {
                objKey = 'frame';
            }
            else
            {
                var theWin;
                if(element instanceof Window)
                {
                    theWin = element;
                }
                else if (element.ownerDocument && element.ownerDocument.defaultView)
                {
                    theWin = element.ownerDocument.defaultView; 
                }
                else if ( element.body
                    && element.body.childNodes
                    && element.body.childNodes[1]
                    && element.body.childNodes[1].contentWindow )
                {
                    theWin = element.body.childNodes[1].contentWindow;
                }
                while ( theWin.parent != theWin )
                {
                    theWin = theWin.parent;
                }
                objKey = theWin.__testFireWindowName; 
            }
            var keyStr = '';
            args.objKey = objKey;
            if ( htmlFinder[htmlFinderName].getVariableKey )
            {
                keyStr = 'key="' + htmlFinder[htmlFinderName].getVariableKey(Firebug.TestFire, args) + '" ';
                keyStr += 'keyType="' + htmlFinder[htmlFinderName].keyType + '" ';
            }

            if ( htmlFinder[htmlFinderName].getObjectKey )
            {

                objKey = htmlFinder[htmlFinderName].getObjectKey(Firebug.TestFire, args);
            }
            if(theWin)
                Firebug.TestFire.lastWindowUsedName = objKey;

            var xml = new Array();

            var finder = this.createFinder(element, args);

            xml.push('<tf:' + xmlElName + ' ' + keyStr + ' useObjectKey="' + objKey + '">');
            var type = htmlFinder._type;
            if ( finder )
            {
                xml.push("<tf:" + type + ">");
                xml.push("<tf:Finder>");
                xml.push('<tf:' + finder.type + ' value="' + this.xmlEncode(finder.value) + '" />');
                xml.push("</tf:Finder>");
            }

            var str = "<tf:" + htmlFinderName;
            var attrs = null;
            if ( htmlFinder[htmlFinderName].getAttributeValues )
            {
                attrs = htmlFinder[htmlFinderName].getAttributeValues(element, args);
            }
            if ( attrs )
            {
                var allNull = true;
                for ( var x in attrs )
                {
                    if ( attrs[x] != null )
                    {
                        allNull = false;
                        break;
                    }
                }
                if ( allNull )
                {
                    // abort!
                    return null;
                }
                else
                {
                    str = this.addAttrs(str, attrs);
                }
            }
            str += " />";
            xml.push(str);
            if ( finder )
            {
                xml.push("</tf:" + type + ">");
            }
            xml.push("</tf:" + xmlElName + ">");
            return xml;
        }
        catch( ex )
        {
            Firebug.TestFire.util.tf_debug(ex);
        }
        return null;
    },

/**
 * Creates the actual finder object, such as xpath('value'), name('value') etc.
 * @todo allow the user to set the preferred search order for finding elements.
 * Preferred order:
 * title
 * value // Non-Text based INPUT types only.
 * name
 * id // ID is usually auto-generated in large webapps.
 *
 *
 *
 *
 */
    createFinder: function( element, args )
    {
        try
        {
            if ( element == FirebugContext.window )
            {
                return null;
            }
            if(this.isFrame(element)){
                return {type: 'index', value: args.index };
            }
            var attr = element.getAttribute('title');
            if ( attr && attr.length > 0 )
            {
                return {type: 'title', value:this.xmlEncode(attr)};
            }
            if ( element.type != "text" )
            {
                attr = element.getAttribute('value');
                if ( attr && attr.length > 0 )
                {
                    return {type: 'value', value:this.xmlEncode(attr)};
                }
            }
            attr = element.getAttribute('name');
            if ( attr && attr.length > 0 )
            {
                return {type: 'name', value:this.xmlEncode(attr)};
            }
            attr = element.getAttribute('id');
            if ( attr && attr.length > 0 )
            {
                return {type: 'id', value:this.xmlEncode(attr)};
            }
            var xpath = FBL.getElementXPath(element, true);
            return {type: 'xpath', value:xpath};
        }
        catch( ex )
        {
            Firebug.TestFire.util.tf_debug(ex);
        }
        return null;

    },





    isFrame: function( element )
    {
        return element.nodeType == 9;
    },

    getFrameActions: function( element, actionableParent )
    {
        // recurse up the tree to map frames.
        // We add each frame from 'the bottom up', deepest goes into the stack first.
        // remember, we have to RETRIEVE them from the top down, so we reverse the array
        // before returning it.
        var hasFrame = false;
        var actionables = [];


        var depth = 0;
        for ( ; element && (element.nodeType == 1 || element.nodeType == 9); element = element.parentNode )
        {
            if ( element.nodeType == 9 )
            { // frame case
                hasFrame = true;
                var frameElement = element.defaultView.frameElement;
                if ( !frameElement )
                {
                    break;
                }
                var idx = this.getFrameIndex(frameElement);
                actionables.push(new Actionable(actionableParent, "frame", {index: idx, depth: depth++}));
                element = frameElement
            }
        }
        if ( hasFrame )
        {
            actionables = actionables.reverse();
            for(var i = 0, L= actionables.length; i < L; i++){
                actionables[i].args.maxDepth = depth - 1;
                actionables[i].populate(actionableParent, element, Frame, 'frame');
            }
        }
        return actionables;

    },

    getFrameIndex: function( element )
    {
        var index = 0;
    // count the previous siblings which are of FRAME type.
        for ( var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling )
        {
            if ( sibling.localName == element.localName )
                ++index;
        }
    // Now count each FRAME which is a child of a FRAMESET that is sibling
        // to the FRAMESET which owns our FRAME - these are 'siblings' in that
        // neither Watij nor Watir has a FRAMESET object.
        element = element.parentNode;

        for ( ; element && element.nodeType == 1 && element.localName.toLowerCase() != "html";
                element = element.parentNode )
        {
            for ( sibling = element.previousSibling; sibling; sibling = sibling.previousSibling )
            {
                if ( !sibling.localName )
                {
                    continue;
                }
                if ( sibling.localName.toLowerCase() == "frame" )
                {
                    ++index;
                }
                else if ( sibling.localName.toLowerCase() == "frameset" )
                {
                    index = index + this.getChildFrameCount(element); // the curse of no FrameSet object.
                }
            }
        }
        return index;
    },

    addAttrs: function( str, attrs )
    {
        for ( var name in attrs )
        {
            var attr = this.xmlEncode(attrs[name]);
            str += " " + name + '="' + attr + '"';
        }
        return str
    },
    escapeHTML: function( str )
    {
        return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    },

    xmlEncode: function ( value )
    {
        if ( !value )
            return value;
        else
            return String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
    }

}